/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    sha256.c
 *  @brief   Secure hash algorithm 256 source file.
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

#include <string.h>
#include "sha256.h"
#include "common.h"

/* Macro to access the workspace as a circular buffer */
#define W(t) w[(t) & 0x0F]
/* SHA-256 auxiliary functions */
#define CH(x, y, z)  (((x) & (y)) | (~(x) & (z)))
#define MAJ(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define ROR32(a, n)  (((a) >> (n)) | ((a) << (32 - (n))))
#define SHL32(a, n)  ((a) << (n))
#define SHR32(a, n)  ((a) >> (n))
#define SIGMA1(x)    (ROR32(x, 2) ^ ROR32(x, 13) ^ ROR32(x, 22))
#define SIGMA2(x)    (ROR32(x, 6) ^ ROR32(x, 11) ^ ROR32(x, 25))
#define SIGMA3(x)    (ROR32(x, 7) ^ ROR32(x, 18) ^ SHR32(x, 3))
#define SIGMA4(x)    (ROR32(x, 17) ^ ROR32(x, 19) ^ SHR32(x, 10))

/* SHA-256 object identifier (2.16.840.1.101.3.4.2.1) */
const uint8_t sha256oid[9] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};

/* SHA-256 padding */
static const uint8_t padding[64] = {
	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

/* SHA-256 constants */
static const uint32_t k[64] = {
	0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
	0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
	0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
	0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
	0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
	0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
	0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
	0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
};

/**
 * Initialize SHA-256 message digest context
 * @context Pointer to the SHA-256 context to initialize
 **/
void sha256_init(struct sha256_context *context)
{
	/* Set initial hash value */
	context->h[0] = 0x6A09E667;
	context->h[1] = 0xBB67AE85;
	context->h[2] = 0x3C6EF372;
	context->h[3] = 0xA54FF53A;
	context->h[4] = 0x510E527F;
	context->h[5] = 0x9B05688C;
	context->h[6] = 0x1F83D9AB;
	context->h[7] = 0x5BE0CD19;

	/* Number of bytes in the buffer */
	context->size = 0;
	/* Total length of the message */
	context->total_size = 0;
}

/**
 * Digest a message using SHA-256
 * @data Pointer to the message being hashed
 * @length Length of the message
 * @digest Pointer to the calculated digest
 **/
void sha256_compute(const void *data, size_t length, uint8_t *digest)
{
	struct sha256_context context;

	sha256_init(&context);
	sha256_update(&context, data, length);
	sha256_final(&context, digest);
}

/**
 * Update the SHA-256 context with a portion of the message being hashed
 * @context Pointer to the SHA-256 context
 * @data Pointer to the buffer being hashed
 * @length Length of the buffer
 **/
void sha256_update(struct sha256_context *context, const void *data, size_t length)
{
	size_t n;

	while (length > 0) {
		/* The buffer can hold at most 64 bytes */
		n = min(length, 64 - context->size);

		/* Copy the data to the buffer */
		memcpy(context->buffer + context->size, data, n);

		/* Update the SHA-256 context */
		context->size += n;
		context->total_size += n;
		/* Advance the data pointer */
		data = (uint8_t *) data + n;
		/* Remaining bytes to process */
		length -= n;
		/* Process message in 16-word blocks */
		if (context->size == 64) {
			/* Transform the 16-word block */
			sha256_process_block(context);
			/* Empty the buffer */
			context->size = 0;
		}
	}
}

/**
 * Finish the SHA-256 message digest
 * @context Pointer to the SHA-256 context
 * @digest Calculated digest (optional parameter)
 **/
void sha256_final(struct sha256_context *context, uint8_t *digest)
{
	int i;
	size_t padding_size;
	uint64_t total_size;

	/* Length of the original message (before padding) */
	total_size = context->total_size * 8;

	/* Pad the message so that its length is congruent to 56 modulo 64 */
	if (context->size < 56) {
		padding_size = 56 - context->size;
	} else {
		padding_size = 64 + 56 - context->size;
	}

	/* Append padding */
	sha256_update(context, padding, padding_size);

	/* Append the length of the original message */
	context->w[14] = cpu_to_be32((uint32_t) (total_size >> 32));
	context->w[15] = cpu_to_be32((uint32_t) total_size);

	/* Calculate the message digest */
	sha256_process_block(context);

	/* Convert from host byte order to big-endian byte order */
	for (i = 0; i < 8; i++) {
		context->h[i] = cpu_to_be32(context->h[i]);
	}

	/* Copy the resulting digest */
	if (digest != NULL) {
		memcpy(digest, context->digest, SHA256_DIGEST_SIZE);
	}
}

/**
 * Finish the SHA-256 message digest (no padding added)
 * @context Pointer to the SHA-256 context
 * @digest Calculated digest
 **/
void sha256_final_raw(struct sha256_context *context, uint8_t *digest)
{
	int i;

	/* Convert from host byte order to big-endian byte order */
	for (i = 0; i < 8; i++) {
		context->h[i] = cpu_to_be32(context->h[i]);
	}

	/* Copy the resulting digest */
	memcpy(digest, context->digest, SHA256_DIGEST_SIZE);

	/* Convert from big-endian byte order to host byte order */
	for (i = 0; i < 8; i++) {
		context->h[i] = be32_to_cpu(context->h[i]);
	}
}

/**
 * Process message in 16-word blocks
 * @context Pointer to the SHA-256 context
 **/
void sha256_process_block(struct sha256_context *context)
{
	int t;
	uint32_t temp1;
	uint32_t temp2;

	/* Initialize the 8 working registers */
	uint32_t a = context->h[0];
	uint32_t b = context->h[1];
	uint32_t c = context->h[2];
	uint32_t d = context->h[3];
	uint32_t e = context->h[4];
	uint32_t f = context->h[5];
	uint32_t g = context->h[6];
	uint32_t h = context->h[7];

	/* Process message in 16-word blocks */
	uint32_t *w = context->w;

	/* Convert from big-endian byte order to host byte order */
	for (t = 0; t < 16; t++) {
		w[t] = be32_to_cpu(w[t]);
	}

	/* SHA-256 hash computation (alternate method) */
	for (t = 0; t < 64; t++) {
		/* Prepare the message schedule */
		if (t >= 16) {
			W(t) += SIGMA4(W(t + 14)) + W(t + 9) + SIGMA3(W(t + 1));
		}

		/* Calculate T1 and T2 */
		temp1 = h + SIGMA2(e) + CH(e, f, g) + k[t] + W(t);
		temp2 = SIGMA1(a) + MAJ(a, b, c);

		/* Update the working registers */
		h = g;
		g = f;
		f = e;
		e = d + temp1;
		d = c;
		c = b;
		b = a;
		a = temp1 + temp2;
	}

	/* Update the hash value */
	context->h[0] += a;
	context->h[1] += b;
	context->h[2] += c;
	context->h[3] += d;
	context->h[4] += e;
	context->h[5] += f;
	context->h[6] += g;
	context->h[7] += h;
}

